DAY 9 已能用 /projects 查看資料。今天加入 /start project_id,讓 Bot 檢查專案 ID 與狀態,成功後記住這個 Discord 伺服器正在處理哪個專案。
guild_projects.json 保存 Guild 的目前專案ProjectMeetingService
/start project_id
目前有四種狀態:
| 程式值 | JSON | 顯示文字 |
|---|---|---|
PENDING |
pending |
待處理 |
IN_PROGRESS |
in_progress |
進行中 |
COMPLETED |
completed |
已完成 |
CANCELLED |
cancelled |
已取消 |
class ProjectStatus(str, Enum):
PENDING = "pending"
IN_PROGRESS = "in_progress"
COMPLETED = "completed"
CANCELLED = "cancelled"
/start 只允許 PENDING → IN_PROGRESS。已完成、已取消或進行中的專案都不能再次啟動。狀態規則集中在 can_transition_to(),避免各處自行判斷。
DAY 9 只能列出專案,今天再加入:
class ProjectRepository(Protocol):
def list_projects(self) -> list[Project]: ...
def get_project(self, project_id: str) -> Project | None: ...
def update_status(
self,
project_id: str,
status: ProjectStatus,
) -> Project: ...
get_project() 會移除空白並轉成大寫,因此 prj-001 也能找到 PRJ-001。update_status() 更新 projects.json,重新啟動 Bot 後仍能保留狀態。
兩份檔案保存不同資訊:
projects.json
└── PRJ-001 目前的專案狀態
guild_projects.json
└── Guild 123456 正在使用哪個專案
同一個 Bot 可能加入多個 Discord 伺服器,所以不能只用一個全域的 current_project_id。Store 會以 Guild ID 為 key,記錄專案 ID 與 UTC 啟動時間。文章中的 Guild ID 只是格式範例。
ProjectMeetingService 集中處理啟動流程:
整理 project_id
↓
讀取 Guild 目前專案
↓
查詢並檢查專案狀態
↓
更新 projects.json
↓
寫入 guild_projects.json
Discord 指令只負責接收輸入與顯示結果,規則則能直接用單元測試驗證。
start_project() 內使用 asyncio.Lock,避免同一個 Bot 程序同時收到兩次 /start 時一起通過檢查。這把鎖無法保護多個 Bot 實例;改成多程序後,仍需要資料庫交易或跨程序鎖。
啟動時要更新兩份 JSON。若 Guild 狀態寫入失敗,程式會嘗試把專案改回原狀。這只是 JSON 階段的補救,不等於真正的交易。
指令接收 project_id,並限制只能在 Discord 伺服器中執行:
@bot.tree.command(name="start", description="啟動指定專案的會議")
@app_commands.describe(project_id="要啟動的專案 ID,例如 PRJ-001")
async def start_command(
interaction: discord.Interaction,
project_id: str,
) -> None:
if interaction.guild_id is None:
await interaction.response.send_message(
"❌ /start 只能在 Discord 伺服器中使用。",
ephemeral=True,
)
return
接著先用 defer() 回應 Discord,再交給 Service:
await interaction.response.defer(thinking=True, ephemeral=True)
project = await meeting_service.start_project(
interaction.guild_id,
project_id,
)
第一次回應已由 defer() 完成,成功與失敗訊息都要改用 followup.send()。
啟動成功後,Bot 會以私密 Embed 顯示專案名稱、狀態與期限。


找不到 ID、Guild 已有專案,或狀態不能啟動時,Service 會回傳可直接顯示的錯誤訊息。
找不到專案 ID:PRJ-999,請先使用 /projects 查看。
這個伺服器已有進行中會議:PRJ-002。

/start 會檢查專案是否存在及狀態是否合法,再更新 projects.json 與 guild_projects.json。每個 Guild 同時只保留一個目前專案,啟動規則則集中在 ProjectMeetingService。
目前仍使用 JSON 保存資料。下一篇會建立 Agent 共用的基礎類別。